/**
 * 
 */
package controlPlane;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import multidomain.Domain;
import multidomain.RIB;
import events.CrankbackEvent;
import events.Event;
import events.OBGPDestroyLightpathEvent;
import events.OBGPIntradomainUpdateEvent;
import events.OBGPUpdateEvent;
import events.RouteRequestEvent;
import tracing.OBGPTracer;
import wdmsim.Constants;
import wdmsim.EventScheduler;
import wdmsim.Flow;
import wdmsim.Lightpath;
import wdmsim.Node;
import wdmsim.OXC;
import wdmsim.Path;
import wdmsim.PhysicalTopology;
import wdmsim.Simulator;
import wdmsim.WDMLink;

/**
 * @author alissonslp
 *
 */
public class OBGPSpeaker extends OXC implements MultidomainControlPlane {
	
	/** */
	EventScheduler scheduler = EventScheduler.getInstance();
	/** Flows that have been accepted and are waiting for a decision */
	private Map<Long, Flow> mappedFlows;
	/** Flows that have been accepted into the network */
    private Map<Flow, Path> activeFlows;
    /** */
	private RIB rib;
	private static int tries = 0;
	
	public OBGPSpeaker(int id, int domain, int wvlConversionRange) {
		super(id, domain, wvlConversionRange);
	}

	public RIB getRib() {
		return rib;
	}
	
	public void generateRIB(int id) throws Exception {
		this.rib = new RIB(id);
		initiateUpdateEvent();
		initiateIntradomainUpdateEvent();
		mappedFlows = new HashMap<Long, Flow>();
        activeFlows = new HashMap<Flow, Path>();
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#createMultidomainLightpath(wdmsim.Flow)
	 */
	@Override
	public void createMultidomainLightpath(Flow flow) {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Vector<WDMLink> links = flow.getPath().getLightpath(0).getLinks();
		for(int i=0; i<links.size(); i++) {
			OXC oxc = pt.getNode(links.get(i).getSrcId());
			if(oxc instanceof OBGPSpeaker) {
				((OBGPSpeaker)oxc).acceptFlow(flow);
			}
		}
	}

	private void acceptFlow(Flow flow) {
		if(mappedFlows.containsKey(flow.getId())) {
			mappedFlows.remove(flow.getId());
			activeFlows.put(flow, flow.getPath());
		}
		
	}

	public void initiateUpdateEvent() {
		OBGPUpdateEvent updateEvent = new OBGPUpdateEvent(this);
		scheduler.schedule(updateEvent, (long)(Math.random()*Constants.UPDATE_TIME));
	}
	
	public void initiateIntradomainUpdateEvent() {
		OBGPIntradomainUpdateEvent updateEvent = new OBGPIntradomainUpdateEvent(this);
		scheduler.schedule(updateEvent, (long)(Math.random()*Constants.IN_UPDATE_TIME));
	}

	/* (non-Javadoc)
	 * @see controlPlane.ultidomainControlPlane#generateAbortEvent(events.Event)
	 */
	@Override
	public void generateAbortEvent(Event event) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		OBGPTracer trace = OBGPTracer.getTracerObject();
		Vector<WDMLink> links = ((RouteRequestEvent)event).getFlow().getPath().getLightpath(0).getLinks();
		Flow flow = ((RouteRequestEvent)event).getFlow();
		int wavelength = ((RouteRequestEvent)event).getFlow().getPath().getLightpath(0).getWavelength();
		// Para todo OBGPSpeaker em path verifica se ele tem um mappedFlow igual a flow e desaloca recursos
		//OXC[] oxc = pt.getNodeVector();
		boolean bool = true;
		for(int i=0; i<links.size(); i++) {
			WDMLink link = links.get(i);
			
			bool = link.release(wavelength, flow);
			if(!bool)
				throw new Exception();
			
			// tira o flow de mappedFlow
			if(pt.getNode(link.getSrcId()) instanceof OBGPSpeaker && 
					((OBGPSpeaker)pt.getNode(link.getSrcId())).hasMappedFlow(((RouteRequestEvent)event).getFlow())) {
				((OBGPSpeaker)pt.getNode(link.getSrcId())).removeMappedFlow(flow);
				((OBGPSpeaker)pt.getNode(link.getSrcId())).getRib().set2(((RouteRequestEvent)event).getFlow());
				
			} else if(pt.getNode(link.getDstId()) instanceof OBGPSpeaker && 
					((OBGPSpeaker)pt.getNode(link.getDstId())).hasMappedFlow(((RouteRequestEvent)event).getFlow())) {
				((OBGPSpeaker)pt.getNode(link.getDstId())).removeMappedFlow(flow);
				((OBGPSpeaker)pt.getNode(link.getDstId())).getRib().set2(((RouteRequestEvent)event).getFlow());
			}
		}
		if(bool) {
			trace.reportBlockedCall(event);
			//rib.set(((RouteRequestEvent)event).getFlow());
			
		} else {
			throw new Exception();
		}
		
	}

	public void generateUpdateEvent() {
		OBGPUpdateEvent updateEvent = new OBGPUpdateEvent(this);
		scheduler.schedule(updateEvent, (long)(Constants.UPDATE_TIME));
	}
	
	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateCrankbackEvent(events.RouteRequestEvent)
	 */
	@Override
	public void generateCrankbackEvent(RouteRequestEvent event) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateDestroyLightpathEvent(wdmsim.Flow)
	 */
	@Override
	public void generateDestroyLightpathEvent(Flow flow) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		int[] path = pt.getDomainPathFromPath(flow.getPath());
		
		OBGPSpeaker obgpSpeaker = (OBGPSpeaker)pt.getOutBorderNode(path[0], path[1]);
		
		Node generator = pt.getNode(flow.getSrc());
		Handler handler = obgpSpeaker;
		
		OBGPDestroyLightpathEvent event = new OBGPDestroyLightpathEvent(flow, generator, handler);
		scheduler.schedule(event, Simulator.CURRENT_TIME+flow.getDuration());
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateRouteReply(events.Event)
	 */
	@Override
	public void generateRouteReply(Event event) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#generateRouteRequestEvent(events.Event)
	 */
	@Override
	public void generateRouteRequestEvent(Event event) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handle(events.Event)
	 */
	@Override
	public void handle(Event event) throws Exception {
		if(event instanceof RouteRequestEvent) {
			this.handleRouteRequestEvent((RouteRequestEvent)event);
			
		} else if(event instanceof OBGPDestroyLightpathEvent) {
			this.handleDestroyLightpathEvent((OBGPDestroyLightpathEvent)event);
			
		} else if(event instanceof OBGPUpdateEvent) {
			this.handleUpdateEvent((OBGPUpdateEvent)event);
		
		} else if(event instanceof OBGPIntradomainUpdateEvent) {
			this.handleIntradomainUpdateEvent((OBGPIntradomainUpdateEvent)event);
			
		} else {
			
		}
	}

	private void handleDestroyLightpathEvent(OBGPDestroyLightpathEvent event) throws Exception {
		if(!destroyLightpath(event.getFlow())) {
			throw new Exception();
			
		} else {
			rib.set(event.getFlow());
		}
	}

	private void handleUpdateEvent(OBGPUpdateEvent event) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		if(this.id==((OBGPSpeaker)event.getHandler()).getId()) {
			for(int n=0; n<pt.getNumNodes(); n++) {
				OXC node = pt.getNode(n);
				if((node instanceof OBGPSpeaker) && pt.hasLink(this.id, n) && !pt.belongsSameDomain(this.id, node.getId())) {
					this.getRib().update((OBGPSpeaker)node);
					((OBGPSpeaker)node).intradomainUpdate();
				}
			}
			if((Simulator.CURRENT_TIME+Constants.UPDATE_TIME) < Simulator.SIMULATION_TOTAL_TIME)
				scheduler.schedule(new OBGPUpdateEvent(event), (long)Simulator.CURRENT_TIME+Constants.UPDATE_TIME);
		
		} else {
			throw new Exception();
		}
		
	}
	
	private void handleIntradomainUpdateEvent(OBGPIntradomainUpdateEvent event) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		if(this.id==((OBGPSpeaker)event.getHandler()).getId()) {
			for(int n=0; n<pt.getNumNodes(); n++) {
				if((pt.getNode(n) instanceof OBGPSpeaker) && pt.hasLink(this.id, n) && pt.belongsSameDomain(this.id, pt.getNode(n).getId())) {
					this.getRib().update((OBGPSpeaker)pt.getNode(n));
				}
			}
			if((Simulator.CURRENT_TIME+Constants.IN_UPDATE_TIME) < Simulator.SIMULATION_TOTAL_TIME)
				scheduler.schedule(new OBGPIntradomainUpdateEvent(event), (long)Simulator.CURRENT_TIME+Constants.IN_UPDATE_TIME);
		
		} else {
			throw new Exception();
		}
		
	}
	
	public void intradomainUpdate() throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		for(int n=0; n<pt.getNumNodes(); n++) {
			if((pt.getNode(n) instanceof OBGPSpeaker) && pt.hasLink(this.id, n) && pt.belongsSameDomain(this.id, pt.getNode(n).getId())) {
				this.getRib().update((OBGPSpeaker)pt.getNode(n));
			}
		}
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleAbortEvent(events.Event)
	 */
	@Override
	public void handleAbortEvent(Event event) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleCrankbackEvent(events.CrankbackEvent)
	 */
	@Override
	public void handleCrankbackEvent(CrankbackEvent event) throws Exception {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleRouteReply(events.Event)
	 */
	@Override
	public void handleRouteReply(Event event) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see controlPlane.MultidomainControlPlane#handleRouteRequestEvent(events.RouteRequestEvent)
	 */
	@Override
	public void handleRouteRequestEvent(RouteRequestEvent event) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		OBGPTracer trace = OBGPTracer.getTracerObject();
		Flow flow = ((RouteRequestEvent) event).getFlow();
		int[] multidomainPath = null;
		int[] intradomainPath = null;
		
		// caso seja um oxc do dominio fonte
		if(pt.belongsSameDomain(event.getFlow().getSrc(), this.id)) { 
			boolean bool;
			if(flow.getRequestArrivalTime()==0)
				flow.setRequestArrivalTime(Simulator.CURRENT_TIME);
			int t = -1;
			Flow f = null;
			boolean bool2 = false;
			do {
				t++;
				f = null;
				f = alreadyCreatedLightpath(flow, t);
				if(f!=null) {
					bool2 = addFlowToLightpath(f.getPath(), flow); 
					if(bool2) {
						registerFlowToOBGPSpeakers(flow.getPath(), flow);
						trace.reportFlowInserting(event);
						generateDestroyLightpathEvent(flow);
						
					} else {
						flow.setDomainPath(new Vector<Domain>());
						flow.setPath(new Path());
					}
				}
			}while(bool2==false && f!=null);
			
			if(bool2==false) {
				if(tries<=pt.getNumBorderNodes(pt.getDomainFromNode(this.id).getID())) {
					RIB.TableRow tableRow = rib.getTableRow(pt.getNode(flow.getDst()));
					
					if(tableRow!=null && tableRow.getWavelength()!=-1) {
						multidomainPath = tableRow.getPath();
						int nextHop = tableRow.getNextHop();
						int wavelength = tableRow.getWavelength();
						// se esse for o melhor node de borda
						if(pt.hasLink(this.id, nextHop)) {
							intradomainPath = pt.getIntradomainPath(pt.getDomainFromNode(this.id).getID(), flow.getSrc(), this.getId(), flow.getSrc(), flow.getBw(), wavelength);
							//System.out.println("INTRA Path: "+util.Auxiliar.array2Vector(intradomainPath));
							if(intradomainPath!=null) {
								Lightpath lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), wavelength);
								bool = false;
								if(lp.getNumLinks()>0) {
									flow.addLightpath(lp);
									bool = reserveInterdomainLink(this, (OBGPSpeaker)pt.getInBorderNode(pt.getDomainFromNode(this.id).getID(), multidomainPath[1]), flow, lp.getWavelength());
								}
								// se o lightpath e o interdomainLink foram reservados com sucesso
								if(bool) {
									RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
									fowardEvent.setHandler((Handler)pt.getInBorderNode(pt.getDomainFromNode(this.id).getID(), multidomainPath[1]));
									scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME); // podemos adicionar o delay do enlace interdominio
									mappedFlows.put(flow.getId(), flow);
									rib.update(lp);
									intradomainUpdate();
									//rib.update(flow);
									
								} else {
									if(lp.getNumLinks()>0) {
										if(!releaseResources(event.getFlow())) throw new Exception();
									}
									trace.reportBlockedCall(event);
								}
								
							} else {
								trace.reportBlockedCall(event);
							}
							
						// se outro node de borda for melhor
						} else {
							Domain domain = pt.getDomainFromNode(this.id);
							OXC node = null;
							Vector<OXC> domainNodes = domain.getNodes();
							
							for(int i=0; i<domainNodes.size(); i++) {
								node = domainNodes.get(i);
								if(pt.hasLink(node.getId(), nextHop) && (node instanceof OBGPSpeaker)) {
									tries++;
									event.setHandler((OBGPSpeaker)node);
									((OBGPSpeaker)node).handle(event);
									break;
								}
							}
						}
					
					} else {
						trace.reportBlockedCall(event);
					}
					
				} else {
					trace.reportBlockedCall(event);
				}
				tries = 0;
			} 
		
		// caso seja um oxc do dominio destino
		} else if(pt.belongsSameDomain(event.getFlow().getDst() , this.id)) {
			
			intradomainPath = pt.getIntradomainPath(pt.getDomainFromNode(this.id).getID(), this.id, flow.getDst(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
			
			if(intradomainPath!=null && intradomainPath.length>0) {
				Lightpath lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), flow.getPath().getLightpaths().lastElement().getWavelength());
				if(lp.getNumLinks()>0) {
					flow.addLightpath(lp);
					mappedFlows.put(flow.getId(), flow);
					this.createMultidomainLightpath(flow);
					Simulator.CURRENT_TIME += Constants.DOMAIN_PROCESSING_TIME;
					trace.reportLightpathCreated(event);
					generateDestroyLightpathEvent(flow);
					System.out.println(flow.getId());
					//rib.update(flow);
					
				} else {
					generateAbortEvent(event);
				}
				
			} else {
				generateAbortEvent(event);
			}
			
		// caso seja um oxc intermediario
		} else {
			RIB.TableRow tableRow = rib.getTableRow(pt.getNode(flow.getDst()));
			
			if(tableRow!=null && tableRow.getWavelength()!=-1) {
			
				multidomainPath = tableRow.getPath();
				int nextHop = tableRow.getNextHop();
				int wavelength = event.getFlow().getPath().getLightpath(0).getWavelength();
				boolean bool;
				
				// se esse for o outBorderNode
				if(pt.belongsSameDomain(this.getId(), event.getGenerator().getId())) {
					if(pt.hasLink(this.id, nextHop)) {
						bool = reserveInterdomainLink(this, (OBGPSpeaker)pt.getInBorderNode(pt.getDomainFromNode(this.id).getID(), multidomainPath[1]), flow, wavelength);
						if(bool) {
							RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
							fowardEvent.setHandler((Handler)pt.getInBorderNode(pt.getDomainFromNode(this.id).getID(), multidomainPath[1]));
							scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME); // podemos adicionar o delay do enlace interdominio
							mappedFlows.put(flow.getId(), flow);
							rib.update(flow);
							intradomainUpdate();
							
						} else {
							generateAbortEvent(event);
						}
							
					} else {
						generateAbortEvent(event);
					}					
				
				// se esse for o inBorderNode e nao for outBorderNode
				} else if(!pt.belongsSameDomain(this.getId(), event.getGenerator().getId())) {
					Domain domain = pt.getDomainFromNode(this.id);
					OXC node = null;
					Vector<OXC> domainNodes = domain.getNodes();
					
					for(int i=0; i<domainNodes.size(); i++) {
						// encontro o node de borda de saida
						node = domainNodes.get(i);
						if(pt.hasLink(node.getId(), nextHop) && (node instanceof OBGPSpeaker)) {
							// calculo o caminho intradominio
							intradomainPath = pt.getIntradomainPath(pt.getDomainFromNode(this.id).getID(), this.getId(), node.getId(), flow.getSrc(), flow.getBw(), wavelength);
							if(intradomainPath!=null && intradomainPath.length>0) {
								Lightpath lp = createIntradomainLightpath(intradomainPath, flow.getSrc(), flow.getSrc(), flow.getBw(), wavelength);
								// repasso a responsabilidade para o outBorderNode
								if(lp.getNumLinks()>0) {
									flow.addLightpath(lp);
									RouteRequestEvent fowardEvent = new RouteRequestEvent(flow, this);
									fowardEvent.setHandler((OBGPSpeaker)node);
									scheduler.schedule(fowardEvent, Simulator.CURRENT_TIME+Constants.DOMAIN_PROCESSING_TIME);
									//rib.update2(flow, lp);
									//intradomainUpdate();
									break;
									
								} else {
									generateAbortEvent(event);
								}
								
							} else {
								generateAbortEvent(event);
							}
						}
					}
				} else {
					throw new Exception();
				}
			}
		}
	}	

	private boolean destroyLightpath(Flow flow) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Lightpath lightpath = flow.getPath().getLightpath(0);
		Vector<WDMLink> links = lightpath.getLinks();
		boolean bool = true;
		
		for(int i=0; i<links.size(); i++) {
			WDMLink link = links.get(i);
			OXC linkSrc = pt.getNode(link.getSrcId());
			
			if(!link.release(lightpath.getWavelength(), flow)) {
				bool = false;
				break;	
			}
			if(linkSrc instanceof OBGPSpeaker) {
				((OBGPSpeaker)linkSrc).activeFlows.remove(flow);
			}
		}
		return bool;
	}
	
	private boolean releaseResources(Flow flow) {
		Path path = flow.getPath();
		boolean released = false;
		
		released = true;
		for(int i=0; i<path.getLightpaths().size(); i++) {
			if(!path.getLightpaths().get(i).releaseLightpath(flow)) {
				released = false;
				break;
			}
		}
		
		return released;
		
	}

	private void registerFlowToOBGPSpeakers(Path path, Flow flow) {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Vector<Lightpath> lpVector = path.getLightpaths();
		
		for(int i=0; i<lpVector.size(); i++) {
			Lightpath lp = lpVector.get(i);
			int j;
			for(j=0; j<lp.getNumLinks(); j++) {
				WDMLink link = lp.getLink(j);
				OXC node = pt.getNode(link.getSource());
				if(node instanceof OBGPSpeaker) {
					((OBGPSpeaker)node).activeFlows.put(flow, flow.getPath());
				}
			}
			
		}
	}

	/**
	 * @param generator
	 * @param pce
	 * @param flow
	 * @param wavelength
	 * @return
	 */
	private boolean reserveInterdomainLink(OBGPSpeaker generator, OBGPSpeaker handler, Flow flow, int wavelength) {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Vector<WDMLink> interdomainLinkVector = pt.getInterdomainLinkVector();
		boolean r = false;
		Lightpath lp = new Lightpath();
		for(int i=0; i<interdomainLinkVector.size(); i++) {
			WDMLink link = interdomainLinkVector.get(i);
			// se o link tiver src igual a generator e dst igual a handler
			if(link.getSrcId()==generator.getId() &&
					link.getDstId()==handler.getId()) {
				// reserva recursos caso haja suficiente em wavelength
				r = link.reserveResource(wavelength, flow.getBw(), flow.getSrc());
				if(r) {
					lp.setWavelength(wavelength);
					lp.addLink(link);
					flow.addLightpath(lp);
				}
				break;
				
			}
		}
		return r;
	}

	private Lightpath createIntradomainLightpath(int path[], int src, int flowSrc, int resources, int wavelength) throws Exception {
		PhysicalTopology pt = PhysicalTopology.getInstance();
		Lightpath lightpath = new Lightpath();
		WDMLink link;
		boolean bool = false;
		int w = wavelength; int i;
		// verifica se em todos os enlaces de path ha bandwidth disponivel em wavelength
		//while(!bool) {
			for(i=0; i<path.length-1; i++) {
				link = pt.getLink(path[i], path[i+1]);
				if(link.getWavelengthAvailableBandwidth(w)>=resources && link.getOwn(wavelength)==-1) {
					bool = true;
					
				} else {
					bool = false;
					break;
				}
			}
			
		//}
		if(bool) {
			lightpath.setWavelength(w);
			for(int j=0; j<path.length-1; j++) {
				link = pt.getLink(path[j], path[j+1]);				
				if(link.reserveResource(w, resources, flowSrc)) {
					lightpath.addLink(link);
					
				} else {
					throw new Exception();
				}
			}
		}
		return lightpath;
	}
	
	private Flow alreadyCreatedLightpath(Flow flow, int trie) throws CloneNotSupportedException {
		Set<Flow> sf = activeFlows.keySet();
		Iterator<Flow> it = sf.iterator();
		int tries = -1;
		
		while(it.hasNext()) {			
			Flow f = it.next();
			if(f.getSrc()==flow.getSrc() && f.getDst()==flow.getDst()) {
				tries++;
				if(tries==trie) {
					flow.setDomainPath((Vector<Domain>)f.getDomainPath().clone());
					flow.setPath((Path)f.getPath().clone());
					return f;
				}
			}
		}
		
		return null;
	}
	
	private boolean addFlowToLightpath(Path path, Flow flow) {
		Vector<Lightpath> lpVector = path.getLightpaths();
		boolean bool = false;
		
		for(int i=0; i<lpVector.size(); i++) {
			Lightpath lp = lpVector.get(i);
			int j;
			for(j=0; j<lp.getNumLinks(); j++) {
				WDMLink link = lp.getLink(j);
				bool = link.addFlow(lp.getWavelength(), flow.getBw(), flow.getSrc());
				if(!bool)
					break;
			}
			if(!bool) {
				for(int k=0; k<j; k++) {
					WDMLink link = lp.getLink(k);
					link.release(lp.getWavelength(), flow);
				}
				break;
			}
		}
		return bool;
	}

	private boolean hasMappedFlow(Flow flow) {
		if(mappedFlows.containsKey(flow.getId())) {
			return true;
			
		} else {
			return false;
		}
	}
	
	public void removeMappedFlow(Flow flow) {
		if(hasMappedFlow(flow)) {
			mappedFlows.remove(flow.getId());
		}
	}
}
